﻿using System;
using System.Collections.Generic;
using System.Linq;

namespace KxParser.Parser.LR0
{
    public class State
    {
        public int Id { get; set; }
        private List<ProductionItem> ProductionItemList;

        public State(int id)
        {
            Id = id;

            if(ProductionItemList==null)
                ProductionItemList=new List<ProductionItem>();
        }

        public string [] GetNextPaths()
        {
            return ProductionItemList.Select(productionItem => productionItem.GetRightValue()).Where(rightValue => !string.IsNullOrEmpty(rightValue) && !rightValue.Equals(Global.Ender)).Distinct().ToArray();
            /*
            return (from nonTerminal in Global.NonTerminals
                                     from productionItem in GetProductionItems()
                                     where productionItem.GetRightValue() == nonTerminal
                                     select nonTerminal).Union(from terminal in Global.Terminals
                                                               from productionItem in GetProductionItems()
                                                               where productionItem.GetRightValue() == terminal
                                                               select terminal).Distinct().ToArray();
             */
        }

        public ProductionItem[] GetProductionItems()
        {
            return ProductionItemList.ToArray();
        }

        public bool CompatibleStateExists(ProductionItem[] productionItems)
        {
            bool masterItemFound;
            foreach (var itemMaster in GetProductionItems())
            {
                masterItemFound = false;
                foreach (var itemSlave in productionItems)
                {
                    if (itemMaster.Equals(itemSlave))
                        masterItemFound = true;
                }
                if (!masterItemFound)
                    return false;
            }
            return true;
        }

        
        public void AddProductionItems(ProductionItem[] items)
        {
            ProductionItemList.AddRange(items);
        }
        

        //public void AddProductionItem(ProductionItem productionItem)
        //{

        //    if(ProductionItemList.Contains(productionItem))
        //        return;

        //    productionItem.CurrentState = this;

        //    ProductionItemList.Add(productionItem);

        //    // If non-terminals occur right from demarkator, we need to include them
        //    var alreadyAddedNonTerminals = new List<string>();

        //        var rightValue = productionItem.GetRightValue(); // Value right from demarkator

        //        if (rightValue != null && productionItem.IsNonTerminal(rightValue) && !alreadyAddedNonTerminals.Contains(rightValue))
        //        {
        //            alreadyAddedNonTerminals.Add(rightValue);
        //            var additionalProductionItems = GetProductionItemsByName(rightValue);

        //            foreach (var additionalProductionItem in additionalProductionItems)
        //            {
        //                var newPid = new ProductionItem(additionalProductionItem);
        //                AddProductionItem(newPid);
        //            }
        //        }
        //}

        public ProductionItem[] GetItemsPlusDependants(ProductionItem item)
        {
            return GetItemsPlusDependants(new[] {item});
        }

        public ProductionItem[] GetItemsPlusDependants(ProductionItem[] items)
        {
            var pids = new List<ProductionItem>();
            var alreadyAddedNonTerminals = new List<string>();

            var queue = new Queue<ProductionItem>();
            foreach (var productionItem in items)
            {
                var newPid = new ProductionItem(productionItem);
                pids.Add(newPid);
                queue.Enqueue(newPid);    
            }
            

            while (queue.Count > 0)
            {
                var currProductioItem = queue.Dequeue();

                //alreadyAddedNonTerminals.Clear();
                var rightValue = currProductioItem.GetRightValue();
                if (rightValue != null && currProductioItem.IsNonTerminal(rightValue) &&
                    !alreadyAddedNonTerminals.Contains(rightValue))
                {
                    alreadyAddedNonTerminals.Add(rightValue);
                    var additionalProductionItems = GetProductionItemsByName(rightValue);

                    foreach (var additionalProductionItem in additionalProductionItems)
                    {
                        var newPid = new ProductionItem(additionalProductionItem);
                        pids.Add(newPid);
                        queue.Enqueue(newPid);
                    }
                }
            }

            foreach (var productionItem in pids)
            {
                productionItem.CurrentState = this;
            }

            return pids.ToArray();
        }

        ProductionItem[] GetProductionItemsByName(string name)
        {
            return Global.ProductionItems.Where(productionItem => productionItem.Name.Equals(name)).ToArray();
        }
        

        public override string ToString()
        {
            var s = Environment.NewLine + string.Format("State Id={0} - ", Id);

            return GetProductionItems().Aggregate(s, (current, productionItem) => current + (Environment.NewLine + productionItem));
        }
    }
}
